<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <title>{% block title %}网络运维系统{% endblock %}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>网络设备拓扑图</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css">
    <style>
        body {
            font-family: 'Arial', sans-serif;
            background-color: #f8f9fa;
        }

        .navbar-brand {
            font-weight: bold;
        }

        .navbar-text {
            font-size: 1rem;
        }

        .container {
            max-width: 960px;
        }

        .alert {
            margin-top: 1rem;
        }

        .topology-container {
            position: relative;
            height: 600px;
            background-color: #f8f9fa;
            border: 1px solid #dee2e6;
            border-radius: 5px;
            overflow: hidden;
        }

        .node {
            position: absolute;
            cursor: move;
            user-select: none;
        }

        .device-node {
            width: 120px;
            height: 60px;
            background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-weight: bold;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
            transition: transform 0.1s ease;
        }

        .device-node:hover {
            transform: scale(1.05);
            box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
        }

        .interface-node {
            width: 30px;
            height: 30px;
            background: linear-gradient(135deg, #00c6ff 0%, #0072ff 100%);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 10px;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
        }

        .connection {
            position: absolute;
            pointer-events: none;
        }

        .vlan-label {
            position: absolute;
            background-color: rgba(255, 255, 255, 0.8);
            padding: 2px 6px;
            border-radius: 4px;
            font-size: 12px;
            font-weight: bold;
            color: #333;
            transform: translate(-50%, -50%);
        }

        #topologyDebug {
            max-height: 200px;
            overflow-y: auto;
            font-family: monospace;
            font-size: 14px;
        }

        .debug-info {
            margin-bottom: 5px;
            padding: 5px;
            border-radius: 4px;
        }

        .text-success {
            color: #28a745;
        }

        .text-error {
            color: #dc3545;
            background-color: rgba(220, 53, 69, 0.1);
        }

        .card-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
    </style>
</head>

<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark shadow-sm">
        <div class="container">
            <a class="navbar-brand" href="{{ url_for('index') }}">网络运维系统</a>
            {% if current_user.is_authenticated %}
            <div class="navbar-text text-light ms-auto me-3">
                欢迎, {{ current_user.username }}
            </div>
            <a href="{{ url_for('logout') }}" class="btn btn-outline-light btn-sm">退出</a>
            {% endif %}
        </div>
    </nav>

    <div class="container-fluid mt-4">
        <div class="card mb-4">
            <div class="card-header bg-light">
                <h5 class="mb-0">网络设备拓扑图</h5>
                <button id="refreshBtn" class="btn btn-primary btn-sm">
                    <i class="fa fa-refresh mr-1"></i>刷新拓扑图
                </button>
            </div>
            <div class="card-body">
                <div class="topology-container" id="topologyContainer">
                    <canvas id="topologyCanvas" width="1000" height="600"></canvas>
                </div>
                <div id="topologyDebug" class="mt-3 p-3 bg-light border rounded">
                    <div class="debug-info text-muted">系统初始化完成，等待渲染拓扑图...</div>
                </div>
            </div>
        </div>
    </div>

    <script>
        // 拓扑图配置
        const topologyConfig = {
            deviceSpacing: 200,   // 设备节点间距
            interfaceSpacing: 80, // 接口节点间距
            deviceRadius: 60,     // 设备节点半径
            interfaceRadius: 15,  // 接口节点半径
            lineColor: '#6c757d', // 连接线颜色
            deviceColor: '#4e73df', // 设备节点颜色
            interfaceColor: '#36b9cc' // 接口节点颜色
        };

        // 拓扑图状态
        let topologyState = {
            nodes: [],
            connections: [],
            selectedNode: null,
            isDragging: false,
            dragOffset: { x: 0, y: 0 },
            animationFrame: null
        };

        // 初始化页面
        document.addEventListener('DOMContentLoaded', function () {
            updateDebugInfo("页面初始化完成，开始渲染拓扑图");

            // 绑定刷新按钮事件
            document.getElementById('refreshBtn').addEventListener('click', function () {
                updateDebugInfo("用户点击刷新按钮，重新渲染拓扑图");
                renderTopology();
            });

            // 初始渲染
            renderTopology();
        });

        // 生成模拟数据
        function generateMockData() {
            return {
                "Router-1": [
                    { interface: "Vlanif10", network: "192.168.10.1", mask: "24", nexthop: "192.168.10.2", vlan_id: "10" },
                    { interface: "Vlanif20", network: "192.168.20.1", mask: "24", nexthop: "192.168.20.2", vlan_id: "20" },
                    { interface: "Vlanif30", network: "192.168.30.1", mask: "24", nexthop: "192.168.30.2", vlan_id: "30" }
                ],
                "Switch-1": [
                    { interface: "Vlanif10", network: "192.168.10.2", mask: "24", nexthop: "192.168.10.1", vlan_id: "10" },
                    { interface: "Vlanif40", network: "192.168.40.1", mask: "24", nexthop: "192.168.40.2", vlan_id: "40" }
                ],
                "Switch-2": [
                    { interface: "Vlanif20", network: "192.168.20.2", mask: "24", nexthop: "192.168.20.1", vlan_id: "20" },
                    { interface: "Vlanif50", network: "192.168.50.1", mask: "24", nexthop: "192.168.50.2", vlan_id: "50" }
                ],
                "Firewall-1": [
                    { interface: "Vlanif30", network: "192.168.30.2", mask: "24", nexthop: "192.168.30.1", vlan_id: "30" },
                    { interface: "Vlanif60", network: "192.168.60.1", mask: "24", nexthop: "192.168.60.2", vlan_id: "60" }
                ],
                "Server-1": [
                    { interface: "Vlanif40", network: "192.168.40.2", mask: "24", nexthop: "192.168.40.1", vlan_id: "40" }
                ],
                "Server-2": [
                    { interface: "Vlanif50", network: "192.168.50.2", mask: "24", nexthop: "192.168.50.1", vlan_id: "50" }
                ],
                "Router-2": [
                    { interface: "Vlanif60", network: "192.168.60.2", mask: "24", nexthop: "192.168.60.1", vlan_id: "60" }
                ]
            };
        }

        // 解析拓扑数据
        function parseTopology() {
            const devices = {{ devices_data| tojson
        }};
        const nodes = [];
        const connections = [];

        // 验证数据
        if (!devices || Object.keys(devices).length === 0) {
            throw new Error("设备数据为空或格式不正确");
        }

        // 设备节点
        const devicePositions = {};
        const deviceKeys = Object.keys(devices);
        const centerX = document.getElementById('topologyCanvas').width / 2;
        const centerY = document.getElementById('topologyCanvas').height / 2;

        // 放置设备节点（圆形布局）
        deviceKeys.forEach((deviceName, index) => {
            const angle = (index / deviceKeys.length) * Math.PI * 2;
            const radius = Math.min(centerX, centerY) * 0.7;
            const x = centerX + Math.cos(angle) * radius;
            const y = centerY + Math.sin(angle) * radius;

            const deviceNode = {
                id: deviceName,
                name: deviceName,
                type: 'device',
                x: x,
                y: y,
                width: 120,
                height: 60,
                interfaces: []
            };

            nodes.push(deviceNode);
            devicePositions[deviceName] = { x, y };
        });

        // 接口节点和连接
        deviceKeys.forEach(deviceName => {
            const deviceNode = nodes.find(n => n.id === deviceName);
            const interfaces = devices[deviceName];

            if (!interfaces || !Array.isArray(interfaces) || interfaces.length === 0) {
                updateDebugInfo(`设备 ${deviceName} 没有接口数据`);
                return;
            }

            // 放置接口节点（围绕设备）
            interfaces.forEach((iface, index) => {
                const angle = (index / interfaces.length) * Math.PI * 2;
                const radius = topologyConfig.deviceRadius + topologyConfig.interfaceSpacing;
                const x = deviceNode.x + Math.cos(angle) * radius;
                const y = deviceNode.y + Math.sin(angle) * radius;

                const interfaceNode = {
                    id: `${deviceName}-${iface.interface}`,
                    name: iface.interface,
                    ip: iface.network,
                    vlan: iface.vlan_id,
                    type: 'interface',
                    device: deviceName,
                    x: x,
                    y: y,
                    width: 30,
                    height: 30
                };

                nodes.push(interfaceNode);
                deviceNode.interfaces.push(interfaceNode);

                // 设备到接口的连接
                connections.push({
                    source: deviceNode,
                    target: interfaceNode,
                    type: 'device-interface'
                });
            });
        });

        // 设备间连接（通过接口）
        nodes.forEach(node => {
            if (node.type !== 'interface') return;

            // 查找目标接口
            const targetInterface = nodes.find(n =>
                n.type === 'interface' &&
                n.ip === node.nexthop &&
                n.device !== node.device
            );

            if (targetInterface) {
                connections.push({
                    source: node,
                    target: targetInterface,
                    type: 'interface-interface',
                    vlan: node.vlan
                });
            }
        });

        updateDebugInfo(`拓扑解析完成: 设备=${deviceKeys.length}, 接口=${nodes.length - deviceKeys.length}, 连接=${connections.length}`);

        return { nodes, connections };
        }

        // 渲染拓扑图
        function renderTopology() {
            try {
                // 清空之前的渲染
                if (topologyState.animationFrame) {
                    cancelAnimationFrame(topologyState.animationFrame);
                }

                // 获取Canvas元素
                const canvas = document.getElementById('topologyCanvas');
                if (!canvas) {
                    throw new Error("找不到Canvas元素");
                }

                const ctx = canvas.getContext('2d');
                ctx.clearRect(0, 0, canvas.width, canvas.height);

                // 解析拓扑数据
                const { nodes, connections } = parseTopology();
                topologyState.nodes = nodes;
                topologyState.connections = connections;

                // 绘制连接
                connections.forEach(conn => {
                    if (conn.type === 'device-interface') {
                        drawConnection(ctx, conn.source, conn.target, '#6c757d', 2);
                    } else {
                        drawCurvedConnection(ctx, conn.source, conn.target, '#4e73df', 3, conn.vlan);
                    }
                });

                // 绘制节点
                nodes.forEach(node => {
                    if (node.type === 'device') {
                        drawDeviceNode(ctx, node);
                    } else {
                        drawInterfaceNode(ctx, node);
                    }
                });

                // 添加交互事件
                setupInteractions(canvas);

                updateDebugInfo("拓扑图渲染成功", false, "text-success");
            } catch (error) {
                console.error("拓扑图渲染失败:", error);
                updateDebugInfo("拓扑图渲染失败: " + error.message, true);
            }
        }

        // 绘制设备节点
        function drawDeviceNode(ctx, node) {
            // 绘制背景
            ctx.fillStyle = topologyConfig.deviceColor;
            ctx.beginPath();
            ctx.roundRect(node.x - node.width / 2, node.y - node.height / 2, node.width, node.height, 8);
            ctx.fill();

            // 绘制边框
            ctx.strokeStyle = '#2e59d9';
            ctx.lineWidth = 2;
            ctx.stroke();

            // 绘制设备名称
            ctx.fillStyle = 'white';
            ctx.font = 'bold 14px Arial';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            ctx.fillText(node.name, node.x, node.y);
        }

        // 绘制接口节点
        function drawInterfaceNode(ctx, node) {
            // 绘制圆形
            ctx.fillStyle = topologyConfig.interfaceColor;
            ctx.beginPath();
            ctx.arc(node.x, node.y, topologyConfig.interfaceRadius, 0, Math.PI * 2);
            ctx.fill();

            // 绘制边框
            ctx.strokeStyle = '#2c9faf';
            ctx.lineWidth = 1;
            ctx.stroke();

            // 绘制接口名称
            ctx.fillStyle = 'white';
            ctx.font = '10px Arial';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            ctx.fillText(node.name.replace('Vlanif', ''), node.x, node.y);
        }

        // 绘制直线连接
        function drawConnection(ctx, source, target, color, width) {
            ctx.beginPath();
            ctx.moveTo(source.x, source.y);
            ctx.lineTo(target.x, target.y);
            ctx.strokeStyle = color;
            ctx.lineWidth = width;
            ctx.stroke();
        }

        // 绘制曲线连接
        function drawCurvedConnection(ctx, source, target, color, width, vlan) {
            const midX = (source.x + target.x) / 2;
            const midY = (source.y + target.y) / 2;

            // 计算控制点（创建曲线效果）
            const dx = target.x - source.x;
            const dy = target.y - source.y;
            const distance = Math.sqrt(dx * dx + dy * dy);
            const offset = Math.min(100, distance / 3);

            const angle = Math.atan2(dy, dx);
            const controlX = midX + Math.sin(angle) * offset;
            const controlY = midY - Math.cos(angle) * offset;

            // 绘制曲线
            ctx.beginPath();
            ctx.moveTo(source.x, source.y);
            ctx.quadraticCurveTo(controlX, controlY, target.x, target.y);
            ctx.strokeStyle = color;
            ctx.lineWidth = width;
            ctx.stroke();

            // 绘制箭头
            drawArrow(ctx, target.x, target.y, angle);

            // 绘制VLAN标签
            ctx.fillStyle = '#333';
            ctx.font = 'bold 12px Arial';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'bottom';
            ctx.fillText(`VLAN ${vlan}`, controlX, controlY);
        }

        // 绘制箭头
        function drawArrow(ctx, x, y, angle) {
            const arrowSize = 10;

            ctx.save();
            ctx.translate(x, y);
            ctx.rotate(angle);

            ctx.beginPath();
            ctx.moveTo(0, 0);
            ctx.lineTo(-arrowSize, arrowSize / 2);
            ctx.lineTo(-arrowSize, -arrowSize / 2);
            ctx.closePath();

            ctx.fillStyle = '#4e73df';
            ctx.fill();

            ctx.restore();
        }

        // 设置交互
        function setupInteractions(canvas) {
            // 清除旧的事件监听器
            canvas.onmousedown = null;
            canvas.onmousemove = null;
            canvas.onmouseup = null;
            canvas.onmouseleave = null;

            // 鼠标按下事件
            canvas.onmousedown = function (e) {
                const rect = canvas.getBoundingClientRect();
                const mouseX = e.clientX - rect.left;
                const mouseY = e.clientY - rect.top;

                // 查找点击的节点
                for (let i = topologyState.nodes.length - 1; i >= 0; i--) {
                    const node = topologyState.nodes[i];
                    const isInside = mouseX >= node.x - node.width / 2 &&
                        mouseX <= node.x + node.width / 2 &&
                        mouseY >= node.y - node.height / 2 &&
                        mouseY <= node.y + node.height / 2;

                    if (isInside) {
                        topologyState.selectedNode = node;
                        topologyState.isDragging = true;
                        topologyState.dragOffset = {
                            x: mouseX - node.x,
                            y: mouseY - node.y
                        };
                        break;
                    }
                }
            };

            // 鼠标移动事件
            canvas.onmousemove = function (e) {
                if (!topologyState.isDragging || !topologyState.selectedNode) return;

                const rect = canvas.getBoundingClientRect();
                const mouseX = e.clientX - rect.left;
                const mouseY = e.clientY - rect.top;

                // 更新节点位置
                topologyState.selectedNode.x = mouseX - topologyState.dragOffset.x;
                topologyState.selectedNode.y = mouseY - topologyState.dragOffset.y;

                // 如果是设备节点，移动其接口
                if (topologyState.selectedNode.type === 'device') {
                    topologyState.selectedNode.interfaces.forEach(iface => {
                        const dx = iface.x - topologyState.selectedNode.x;
                        const dy = iface.y - topologyState.selectedNode.y;
                        const distance = Math.sqrt(dx * dx + dy * dy);
                        const angle = Math.atan2(dy, dx);

                        iface.x = topologyState.selectedNode.x + Math.cos(angle) * topologyConfig.interfaceSpacing;
                        iface.y = topologyState.selectedNode.y + Math.sin(angle) * topologyConfig.interfaceSpacing;
                    });
                }

                // 重绘拓扑图
                redrawTopology();
            };

            // 鼠标释放事件
            canvas.onmouseup = function () {
                topologyState.isDragging = false;
                topologyState.selectedNode = null;
            };

            // 鼠标离开事件
            canvas.onmouseleave = function () {
                topologyState.isDragging = false;
                topologyState.selectedNode = null;
            };
        }

        // 重绘拓扑图
        function redrawTopology() {
            const canvas = document.getElementById('topologyCanvas');
            const ctx = canvas.getContext('2d');

            // 清除画布
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            // 绘制连接
            topologyState.connections.forEach(conn => {
                if (conn.type === 'device-interface') {
                    drawConnection(ctx, conn.source, conn.target, '#6c757d', 2);
                } else {
                    drawCurvedConnection(ctx, conn.source, conn.target, '#4e73df', 3, conn.vlan);
                }
            });

            // 绘制节点
            topologyState.nodes.forEach(node => {
                if (node.type === 'device') {
                    drawDeviceNode(ctx, node);
                } else {
                    drawInterfaceNode(ctx, node);
                }
            });
        }

        // 更新调试信息
        function updateDebugInfo(message, isError = false, className = 'text-muted') {
            const debugElement = document.getElementById('topologyDebug');
            if (!debugElement) return;

            const messageElement = document.createElement('div');
            messageElement.className = `debug-info ${isError ? 'text-error' : className}`;
            messageElement.textContent = `${new Date().toLocaleTimeString()} - ${message}`;

            debugElement.appendChild(messageElement);

            // 自动滚动到底部
            debugElement.scrollTop = debugElement.scrollHeight;
        }
    </script>
</body>

</html>